#pragma once

#include "Singleton.h"
#include <functional>
#include <string_view>
#include "CTableCache.h"
#include "ThreadSafeQueue.h"
#include "strings/multi_lang_def.h"
#include "table/char/table_list.h"
#include "table/actvt/table_list.h"
#include "table/world/table_list.h"

class DatabaseMgr : public Singleton<DatabaseMgr>
{
public:
	DatabaseMgr();
	virtual ~DatabaseMgr();

	void SetLang(int lang);
	const std::string& GetText(uint32 id, STRING_TEXT_TYPE type);
	const std::string& GetInstText(uint32 id, STRING_TEXT_TYPE type);

	bool ReloadTable(const std::string_view& name);

	bool AsyncLoadAllTables(size_t threads);

	template<typename T>
	const CTableCache<T>* GetTable() const;

	static void SetupActvtDB();
	static void SetupWorldDB();

	static bool WaitFinishTasks(
		ThreadSafeQueue<std::function<bool()>>& tasks, size_t threads);

private:
	template<typename T> void CreateTable();
	template<typename T> void DeleteTable();
	template<typename T> void AsyncLoadTable();

	void RegisterTablePtr(
		const std::string_view& tblName, ITableCache** pTableCache);

	void AsyncLoadTable(ITableCache* pTableCache);

	CTableCache<inst_string_text_list>* m_pInstStringTextTbl;
	CTableCache<string_text_list>* m_pStringTextTbl;

	CTableCache<Configure>* m_pConfigureTbl;
	CTableCache<Scriptable>* m_pScriptableTbl;

	CTableCache<MapInfo>* m_pMapInfoTbl;
	CTableCache<MapZone>* m_pMapZoneTbl;
	CTableCache<MapGraveyard>* m_pMapGraveyardTbl;
	CTableCache<TeleportPoint>* m_pTeleportPointTbl;
	CTableCache<WayPoint>* m_pWayPointTbl;
	CTableCache<LandmarkPoint>* m_pLandmarkPointTbl;
	CTableCache<ItemPrototype>* m_pItemPrototypeTbl;
	CTableCache<ItemEquipPrototype>* m_pItemEquipPrototypeTbl;
	CTableCache<CharPrototype>* m_pCharPrototypeTbl;
	CTableCache<CreatureSpawn>* m_pCreatureSpawnTbl;
	CTableCache<SObjPrototype>* m_pSObjPrototypeTbl;
	CTableCache<StaticObjectSpawn>* m_pStaticObjectSpawnTbl;
	CTableCache<LootSet>* m_pLootSetTbl;
	CTableCache<LootSetGroup>* m_pLootSetGroupTbl;
	CTableCache<LootSetGroupItem>* m_pLootSetGroupItemTbl;
	CTableCache<LootSetGroupCheque>* m_pLootSetGroupChequeTbl;
	CTableCache<SpellInfo>* m_pSpellInfoTbl;
	CTableCache<SpellLevelInfo>* m_pSpellLevelInfoTbl;
	CTableCache<SpellLevelEffectInfo>* m_pSpellLevelEffectInfoTbl;
	CTableCache<QuestPrototype>* m_pQuestPrototypeTbl;
	CTableCache<QuestCreatureVisible>* m_pQuestCreatureVisibleTbl;
	CTableCache<PlayerBase>* m_pPlayerBaseTbl;
	CTableCache<PlayerAttribute>* m_pPlayerAttributeTbl;
	CTableCache<CreatureAttribute>* m_pCreatureAttributeTbl;
	CTableCache<CreatureCustomAttribute>* m_pCreatureCustomAttributeTbl;
	CTableCache<ShopPrototype>* m_pShopPrototypeTbl;

	ThreadSafeQueue<std::function<bool()>> m_asyncLoadTables;
	std::unordered_map<std::string_view, ITableCache**> m_registerTables;
	std::vector<ITableCache*> m_obsoleteTables;

	int m_lang;
};

#define sDBMgr (*DatabaseMgr::instance())

#define I18N_TEXT(id,type) (sDBMgr.GetText(id, type))
#define I18N_CSTR(id,type) (I18N_TEXT.c_str())
#define I18N_INST_TEXT(id,type) (sDBMgr.GetInstText(id, type))
#define I18N_INST_CSTR(id,type) (I18N_INST_TEXT.c_str())

template <typename T> const T* GetDBEntry(uint32 iIdx) {
	return sDBMgr.GetTable<T>()->GetEntry(iIdx);
}

extern const std::string emptyString;
extern const std::string_view emptyStringView;
